package acs.bridge;

import acs.bridge.dao.BridgeDAOImpl;
import org.apache.log4j.Logger;
import org.springframework.context.support.AbstractXmlApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * User: stas
 * Date: 16.03.13
 * Time: 23:41
 * To change this template use File | Settings | File Templates.
 */
public class Bridge implements BridgeInterface {
    static String version = "4";

    Logger logger = Logger.getLogger(this.getClass().getName());
    private JdbcTemplate jdbcTemplate;
    private BridgeDAOImpl bridgeDAOImpl;
    private String group_tag;
    private String meta_tag;
    private String module_tag;
    private String module_filename;
    private BridgeModuleInterface bridgeModule;

    public void executeBridge() {
        try {
            logger.info("start executeBridge v." + version + " {group_tag=" + group_tag + ",meta_tag=" + meta_tag + "}");
            bridgeDAOImpl.getJdbcTemplate().getDataSource().getConnection().setAutoCommit(false);
        /* Загрузка контекста*/
            load_bridge_module();

            Class[] interfaces = bridgeModule.getClass().getInterfaces();
            boolean l_is_BridgeModuleExtInterface = false;
            for (Class cInterface : interfaces) {
                logger.info(cInterface.getName());

                if (cInterface.getName().equals("acs.bridge.BridgeModuleExtInterface")) {
                    l_is_BridgeModuleExtInterface = true;
                    break;

                }
            }

            /* Запуск первичной обработки */
            if (l_is_BridgeModuleExtInterface) {
                ((BridgeModuleExtInterface) bridgeModule).initial_processing();
            }

              /* Запуск обработки интерфейса */
            bridgeDAOImpl.prc_start_meta_processing(group_tag, meta_tag);
            BigDecimal taskID = bridgeDAOImpl.get_task_id();
            logger.info("log_sid=" + bridgeDAOImpl.get_log_sid());
            logger.info("task_id=" + taskID);

            if (l_is_BridgeModuleExtInterface)
                ((BridgeModuleExtInterface) bridgeModule).execute_meta_start(group_tag, meta_tag);
            /* Цикл обработки транзакций */
            transactionLoop();

            if (l_is_BridgeModuleExtInterface)
                ((BridgeModuleExtInterface) bridgeModule).execute_meta_finish(group_tag, meta_tag);
            /* Завершение обработки интерфейса */
            bridgeDAOImpl.prc_finish_meta_processing();

            /* Запуск завершающей обработки */
            if (l_is_BridgeModuleExtInterface) {

                String l_schema_name = bridgeDAOImpl.fnc_schema_by_group(group_tag, meta_tag);
                String l_object_name = "FINAL_" + meta_tag.toUpperCase();
                logger.info("Final processing started. {" + l_object_name + "}");
                if (bridgeDAOImpl.get_object_exist(l_schema_name, l_object_name, "PROCEDURE").intValue() > 0) {
                    bridgeDAOImpl.final_processing(taskID, l_schema_name + "." + l_object_name);
                }
                ((BridgeModuleExtInterface) bridgeModule).final_processing(taskID);
                logger.info("Final processing finished.");
            }

        } catch (Exception a_exception) {
            logger.info(a_exception.getMessage());
            try {
                bridgeDAOImpl.prc_processing_error("executeBridge fatal error (" + a_exception.getMessage() + ")");
                //bridgeDAOImpl.msg("Fatal error (external). (" + a_exception.getMessage() + ")", "L", null, 0, "executeBridge_0");
            } catch (Exception a_exception2) {
                logger.info(a_exception2.getMessage());
            }
        }
    }

    private void load_bridge_module() throws SQLException {

        Clob l_clob = bridgeDAOImpl.get_module_config(module_tag);
        AbstractXmlApplicationContext bridge_module_context;

        if (module_filename != null) {
            bridge_module_context = new ClassPathXmlApplicationContext(module_filename);
        } else {
            bridge_module_context = new StreamXmlApplicationContext(l_clob.getAsciiStream());
        }
        bridgeModule = (BridgeModuleInterface) bridge_module_context.getBean("BridgeModule");

        //bridgeModule.execute();
        bridge_module_context.close();

    }

    private void transactionLoop() {
        int count = 0;
        do {
            logger.info("Disable=" + bridgeDAOImpl.fnc_check_disable());

            count = bridgeDAOImpl.fnc_start_transaction().intValue();
            if (count == -1)
                break;

            logger.info("Row count : " + count);

            if (count > 0)
                rawLoop();

            bridgeDAOImpl.prc_finish_transaction();
        }
        while (count > 0);

    }

    private void rawLoop() {
        List<Map> rows = bridgeDAOImpl.get_g_tt_bridge_raw();
        BigDecimal id;
        int count = 1;
        for (Map row : rows) {
            id = (BigDecimal) row.get("ID");
            logger.info("ID=" + id);

            int l_ret = bridgeDAOImpl.fnc_start_raw_processing(id).intValue();

            if (l_ret == 2) {
                logger.info("Command 'INFO'");
                logger.info(clobToString(bridgeDAOImpl.fnc_get_info()));
            }

            if ((bridgeDAOImpl.fnc_is_error() == 0) && (l_ret != -1)) {
            /* Запуск модуля обработки */
                try {
                    bridgeModule.execute(getGroup_tag(), getMeta_tag(), id, getModule_tag());
                } catch (BridgeModuleException a_exception) {
                    String l_log_text = "BridgeModuleException error. (" + a_exception.getMessage() + ")";
                    logger.info(l_log_text);
                    bridgeDAOImpl.set_cur_log_text(l_log_text);
                    bridgeDAOImpl.msg(l_log_text, "L", "{raw_id=" + id + "}", 30, "rawLoop_1", 0);
                } catch (BridgeModuleCriticalException a_exception) {
                    String l_log_text = "BridgeModuleCriticalException error. (" + a_exception.getMessage() + ")";
                    logger.info(l_log_text);
                    bridgeDAOImpl.set_cur_log_text(l_log_text);
                    bridgeDAOImpl.msg(l_log_text, "L", "{raw_id=" + id + "}", 30, "rawLoop_1", 1);
                } catch (Exception a_exception) {
                    String l_log_text = "Processing error (external). (" + a_exception.getMessage() + ")";
                    logger.info(l_log_text);
                    bridgeDAOImpl.set_cur_log_text(l_log_text);
                    bridgeDAOImpl.msg(l_log_text, "L", "{raw_id=" + id + "}", 30, "rawLoop_1", 0);
                }
            }
            bridgeDAOImpl.prc_finish_raw_processing(id, new BigDecimal(count));
            count++;

            if (l_ret == -1) {
                logger.info("Command 'STOP'");
                logger.info("Raw processing interrupted");
                break;
            }
        }
    }


    private String clobToString(java.sql.Clob data) {
        final StringBuilder sb = new StringBuilder();

        try {
            final Reader reader = data.getCharacterStream();
            final BufferedReader br = new BufferedReader(reader);

            int b;
            while (-1 != (b = br.read())) {
                sb.append((char) b);
            }

            br.close();
        } catch (SQLException e) {
            logger.error("SQL. Could not convert CLOB to string (" + e.getMessage() + ")");
            return null;
        } catch (IOException e) {
            logger.error("IO. Could not convert CLOB to string (" + e.getMessage() + ")");
            return null;
        }

        return sb.toString();
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setBridgeDAOImpl(BridgeDAOImpl bridgeDAOImpl) {
        this.bridgeDAOImpl = bridgeDAOImpl;
    }

    public void setGroup_tag(String group_tag) {
        this.group_tag = group_tag;
    }

    public String getGroup_tag() {
        return group_tag;
    }

    public void setMeta_tag(String meta_tag) {
        this.meta_tag = meta_tag;
    }

    public String getMeta_tag() {
        return meta_tag;
    }


    public void setModule_tag(String module_tag) {
        this.module_tag = module_tag;
    }

    public String getModule_tag() {
        return module_tag;
    }

    public void setModule_filename(String module_filename) {
        this.module_filename = module_filename;
    }

    public String getModule_filename() {
        return module_filename;
    }
}
